<template>
    <div class="my-remote-search3 my-input-components">
        <div class="ivu-input-wrapper">
            <input
                :name="name"
                :class="inputClasses"
                class="input-dropdown ivu-input"
                ref="inputDropdown"
                type="text"
                :disabled="disabled"
                :placeholder="placeholder"
                :title="title"
                v-model="keywordInner"
                @input="keywordChange"
                @focus="focusOrBlur($event)"
                @blur="focusOrBlur($event)"
                @keyup="keyup"
                :style="{ width: trueWidth }"
                autocomplete="off"
            />
        </div>
        <transition name="transition-drop">
            <div class="result-list" ref="resultList" :style="dropStyles" v-if="ifShow" @mousedown="handleMousedown">
                <ul v-if="firstData.length===0">
                    <li class="no-result">
                        <span>{{ '暂无结果' }}</span>
                    </li>
                </ul>
                <ul v-else class="input-dropdown-ul">
                    <li
                        :class="{
                            'input-dropdown-hover':keyboardIndex===index,
                            'input-dropdown-disabled':data._disabled
                        }"
                        class="input-dropdown-item"
                        v-for="(data, index) in firstData"
                        @click.stop="click($event, data)"
                        :key="index"
                    >
                        <span>{{data | getOptionLables(labelFieldList)}}</span>
                        <span class="input-dropdown-side" v-if="secondLabels.length">
                            {{ data | getOptionLables(secondLabels)}}
                        </span>
                    </li>
                </ul>
            </div>
        </transition>
    </div>
</template>
<script>
import './myInputComponents.less'
import Popper from 'popper.js'
export default {
    name: 'RemoteSearch',
    data () {
        return {
            isFocus: false,
            keywordInner: '',
            ifShow: false,
            keyboardIndex: -1,
            firstData: [],
            dropWidth: ''
        }
    },
    props: {
        // 表单元素原生 name属性
        name: {
            type: String,
            default: ''
        },
        container: {
            default: 'window'
        },
        value: { // v-model
            required: true
        },
        // 大小
        size: {
            validator (value) {
                return ['small', 'large', 'default'].indexOf(value) >= 0
            },
            default: 'default'
        },
        uwidth: {
            type: [Number, String]
        },
        width: {
            type: [Number, String],
            default: '100%'
        },
        dropdownWidth: {
            type: [Number, String],
            default: ''
        },
        height: {
            type: Number,
            default: 20
        },
        title: {
            type: String,
            default: ''
        },
        placeholder: {
            type: String,
            default: ''
        },
        keyword: {
            type: String,
            default: ''
        },
        list: { // 下拉列表数组
            required: true,
            type: Array
        },
        keywordFiled: { // 显示在输入框里面的字段
            type: String,
            default: ''
        },
        valueField: { // 返回值，为空则返回当前对象
            type: String,
            default: ''
        },
        labelFieldList: { // 下拉选项显示字段，支持多字段显示
            type: Array,
            required: true
        },
        // 第二段需要显示的label 的 key 数组(key 对应list下拉列表数据的字段)
        secondLabels: {
            type: Array,
            default: () => []
        },
        disabled: {
            type: Boolean,
            default: false
        },
        placement: {
            type: String,
            default: 'bottom-start'
        },
        // 禁用选项
        notAllowedList: {
            type: Array,
            default () {
                return []
            }
        },
        // 禁用选项方法 返回 Boolean
        disableMethod: {
            type: Function
        },
        // 下拉框是否使用固定定位
        fixed: {
            type: Boolean,
            default: true
        }
    },
    methods: {
        setValue (data) {
            const valueField = this.valueField
            const keywordFiled = this.keywordFiled
            this._value = data && valueField ? data[valueField] : ''
            this.keywordInner = data && keywordFiled ? data[keywordFiled] : ''
            this.$emit('input', this._value)
            this.$emit('on-get-name', this.keywordInner)
            this.$emit('get-all', data || null)
        },
        // 点击选项
        clickItem (data) {
            if (data._disabled) {
                this.setValue(null)
                return
            }
            this.ifShow = false
            this.setValue(data)
        },
        // 过滤
        keywordChange (event) {
            this.keyboardIndex = -1
            const query = this.keywordInner.replace(/^\s+|\s+$/g, '')
            const oldLen = this.firstData.length
            if (query === '') {
                this.firstData = this.listGroup.slice(0, 20)
                this._value && this.setValue(null)
            } else {
                const wd = query.toUpperCase()
                let obj = {}
                const filerKeys = this.labelFieldList.concat(this.secondLabels)
                this.listGroup.forEach(item => {
                    let i = 0 // 位于 obj 中的序号
                    let len = 0 // 匹配的 label 的长度
                    filerKeys.find((k, j) => {
                        const v = String(item[k] || item[k] === 0 ? item[k] : '').toUpperCase()
                        const index = v.indexOf(wd)
                        if (index >= 0) {
                            i = (j + 1) * 100 + index // 设置obj 的key  根据在 filerKeys 中的序号j, (j + 1)  * 100 再 加上 出现的位置 index 为 索引
                            len = v.length // 获取匹配的字段的长度  根据 i  和 len  进行排序
                            return true
                        }
                        return false
                    })
                    if (i > 0) {
                        if (obj[i] === undefined) {
                            obj[i] = {}
                        }
                        if (obj[i][len] === undefined) {
                            obj[i][len] = []
                        }
                        obj[i][len].push(item)
                    }
                })
                let result = []
                Object.keys(obj).forEach(i => {
                    Object.keys(obj[i]).forEach(j => {
                        Array.prototype.push.apply(result, obj[i][j])
                    })
                })
                this.firstData = result.slice(0, 20)
            }
            if (this.firstData.length) {
                this.keyboardIndex = this.firstData.findIndex(item => !item._disabled)
            }
            this.ifShow = this.isFocus
            if (this.ifShow && oldLen !== this.firstData.length) {
                this.$nextTick(() => {
                    this.updatePopper()
                })
            }
        },
        // 失焦和聚焦
        focusOrBlur (e) {
            if (e.type === 'focus') {
                this.isFocus = true
                e.target.select() // 全选(TAPD:1021868)
            } else {
                // 忽略 单击下拉选项引起的失焦事件
                if (this._down) {
                    e.stopPropagation()
                    this._down = false
                    return false
                };
                this.isFocus = false
                this.ifShow = this.isFocus
                this.handlerBlur()
            }
        },
        // 处理失去光标事件
        handlerBlur () {
            // 判断当前选择的值是否有效
            if (this.value) {
                let target = (this.firstData.length ? this.firstData : this.listGroup).find(item => item[this.valueField] === this.value)
                if (target) {
                    this.keywordInner = target[this.keywordFiled] || ''
                }
            } else {
                this.keywordInner = ''
            }
            // 触发 blur事件 以及 字段校验
            this.$emit('on-blur', this.keywordInner)
            this.dispatch('FormItem', 'on-form-blur', this.value)
        },
        click (event, data) {
            this._timer && clearTimeout(this._timer)
            this.clickItem(data)
            this.handleMouseup()
            this.$nextTick(() => {
                this.$refs.inputDropdown.focus() // 点击后重新 focus 输入框
            })
        },
        keyup (e) {
            // 上键 38
            // 下键 40
            // 回车 13
            // e.stopPropagation()
            let event = e || window.event
            let keyCode = event.which || event.keyCode
            switch (keyCode) {
                case 40:
                    if (!this.ifShow || this.firstData.length === 0) {
                        return
                    }
                    this.navigateOptions(1)
                    break
                case 38:
                    if (!this.ifShow || this.firstData.length === 0) {
                        return
                    }
                    this.navigateOptions(-1)
                    break
                case 13:
                    if (this.ifShow) {
                        if (this.firstData.length) {
                            if (this.keyboardIndex > -1) {
                                this.clickItem(this.firstData[this.keyboardIndex])
                            } else {
                                // fix:默认选中第一个时没有判断有没有旧值的bug
                                this.clickItem(this.firstData[0])
                            }
                        } else {
                            // 不存在则清空已选择的值
                            this.setValue(null)
                        }
                    }
                    this.$emit('on-enter', e)
                    break
                case 27:
                    // ESC 关闭下拉弹窗
                    e.preventDefault()
                    e.stopPropagation()
                    this.ifShow = false
                    break
            }
        },
        navigateOptions (direction) {
            const optionsLength = this.firstData.length - 1

            let index = this.keyboardIndex + direction
            if (index < 0) index = optionsLength
            if (index > optionsLength) index = 0

            // 跳过禁用项
            if (direction > 0) {
                let nearestActiveOption = -1
                for (let i = 0; i < this.firstData.length; i++) {
                    const optionIsActive = !this.firstData[i]._disabled
                    if (optionIsActive) nearestActiveOption = i
                    if (nearestActiveOption >= index) break
                }
                index = nearestActiveOption
            } else {
                let nearestActiveOption = this.firstData.length
                for (let i = optionsLength; i >= 0; i--) {
                    const optionIsActive = !this.firstData[i]._disabled
                    if (optionIsActive) nearestActiveOption = i
                    if (nearestActiveOption <= index) break
                }
                index = nearestActiveOption
            }

            this.keyboardIndex = index
        },
        dispatch (componentName, eventName, params) {
            let parent = this.$parent || this.$root
            let name = parent.$options.name

            while (parent && (!name || name !== componentName)) {
                parent = parent.$parent

                if (parent) {
                    name = parent.$options.name
                }
            }
            if (parent) {
                parent.$emit.apply(parent, [eventName].concat(params))
            }
        },
        // 添加鼠标按下事件
        handleMousedown () {
            this._down = true
            this._timer = setTimeout(() => {
                this.ifShow = false
            }, 300)
        },
        handleMouseup () {
            this._down = false
        },
        createPopper () {
            this._popper = new Popper(
                this.$refs.inputDropdown,
                this.$refs.resultList,
                {
                    placement: this.placement,
                    modifiers: {
                        computeStyle: {
                            gpuAcceleration: false
                        },
                        preventOverflow: {
                            boundariesElement: this.container
                        }
                    },
                    positionFixed: this.fixed,
                    onCreate: () => {
                        this.$nextTick(() => {
                            this._popper.update()
                        })
                    },
                    onUpdate: () => {
                        this.resetTransformOrigin()
                    }
                }
            )
            const element = this.$refs.inputDropdown
            if (element) {
                this.dropWidth = parseInt(element.getBoundingClientRect().width)
            }
        },
        updatePopper () {
            const popper = this._popper
            if (popper) {
                popper.update()
            } else {
                this.createPopper()
            }
        },
        destroyPopper () {
            if (this._popper) {
                this._popper.destroy()
                this._popper = null
            }
        },
        resetTransformOrigin () {
            const popper = this._popper
            if (!popper) return
            let x_placement = popper.popper.getAttribute('x-placement')
            let placementStart = x_placement.split('-')[0]
            let placementEnd = x_placement.split('-')[1]
            const leftOrRight =
                x_placement === 'left' || x_placement === 'right'
            if (!leftOrRight) {
                popper.popper.style.transformOrigin =
                    placementStart === 'bottom' ||
                    (placementStart !== 'top' && placementEnd === 'start')
                        ? 'center top'
                        : 'center bottom'
            }
        }
    },
    created () {
        this._disableOptionMethod = this.disableMethod || ((ele) => {
            return this.notAllowedList.includes(ele[this.valueField])
        })
        // 设置内部 _value 的值
        // 初始化 listGroup 禁用选项
        // 设置 keywordInner 的值
        // input 的 input 事件触发才需要展示下拉框 (TAPD:1021868) 所以不设置 firstData 的值
        this._value = this.value || '' // 内部的value
        // listGroup 保存所有的下拉选项
        // 禁用不可选的下拉
        let keywordInner = ''
        this.listGroup = (this.list || []).map(ele => {
            if (this.value && ele[this.valueField] == this.value) {
                keywordInner = ele[this.keywordFiled] || ''
            }
            return {
                ...ele,
                _disabled: this._disableOptionMethod(ele)
            }
        })
        // 如果有值并且 没有 keyword
        if (this.value && !this.keyword) {
            this.keywordInner = keywordInner
        } else {
            this.keywordInner = this.keyword
        }
    },
    watch: {
        value (val) {
            // 判断是否组件外改变了value
            if (this.listGroup.length && val !== this._value) {
                let temp = null
                if (val || val === 0) {
                    temp = this.firstData.find(item => {
                        return item[this.valueField] == this.value
                    })
                    if (!temp) {
                        temp = this.listGroup.find(item => {
                            return item[this.valueField] == this.value
                        })
                    }
                }
                // 触发value 改变的事件
                this.keywordInner = temp && temp[this.keywordFiled] || ''
            }
            // 同步内部的 _value
            this._value = val
            // 触发 ivew 表单 change 校验
            this.dispatch('FormItem', 'on-form-change', val)
        },
        list (newVal, oldVal) {
            let tempList = newVal ? newVal.slice() : []
            this.listGroup = tempList.map(ele => {
                return {
                    ...ele,
                    _disabled: this._disableOptionMethod(ele)
                }
            })
            this.firstData = this.listGroup.slice(0, 20)
            if (this.value === '') {
                this.keywordInner = ''
            } else {
                let temp = this.listGroup.filter(item => {
                    return item[this.valueField] == this.value
                })
                if (temp.length) {
                    this.keywordInner = temp[0][this.keywordFiled]
                }
            }
            this.$nextTick(() => {
                if (this._popper) this._popper.update()
            })
        },
        keyword (val) {
            this.keywordInner = val || ''
        },
        keyboardIndex (index) {
            if (index < 0 || this.firstData.length === 0) return
            const $el = this.$refs.resultList
            if (!$el) return
            // 更新滚动条
            const optionValue = this.firstData[index][this.valueField]
            let optionInstance = null
            const optionArr = $el.getElementsByClassName('input-dropdown-item')
            const ulEle = $el.getElementsByClassName('input-dropdown-ul')[0]
            for (let i = 0; i < optionArr.length; i++) {
                if (optionArr[i].innerText.indexOf(optionValue) > -1) {
                    optionInstance = optionArr[i]
                    break
                }
            }
            if (!optionInstance) return
            let bottomOverflowDistance = optionInstance.getBoundingClientRect().bottom - ulEle.getBoundingClientRect().bottom
            let topOverflowDistance = optionInstance.getBoundingClientRect().top - ulEle.getBoundingClientRect().top
            if (bottomOverflowDistance > 0) {
                ulEle.scrollTop += bottomOverflowDistance
            }
            if (topOverflowDistance < 0) {
                ulEle.scrollTop += topOverflowDistance
            }
        },
        ifShow (v) {
            if (v) {
                this.$nextTick(() => {
                    // 选项列表使用了v-if 设置popper前需要销毁上一次popper实例
                    this.destroyPopper()
                    this.updatePopper()
                })
            }
        }
    },
    computed: {
        trueWidth () {
            return this.width.toString().indexOf('%') < 0 ? (this.width + 'px') : this.width
        },
        dropStyles () {
            let style = {}
            if (this.dropWidth) style.minWidth = `${this.dropWidth}px`
            if (this.uwidth) {
                style.width = this.uwidth
            }
            return style
        },
        inputClasses () {
            return [
                `ivu-input-${this.size}`
            ]
        }
    },
    filters: {
        getOptionLables (data, keys, flag = ' ') {
            return keys
                .map(key => {
                    return data[key] || ''
                })
                .join(flag)
        }
    },
    beforeDestroy () {
        this._timer && clearTimeout(this._timer)
        this.destroyPopper()
    },
    deactivated () {
        this.$options.beforeDestroy[0].call(this)
    }
}
</script>
